/*
 * Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package taskgraph.tasks;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * A task that can have subordinate tasks.
 * 
 * @author Armando Blancas
 */
public abstract class CompositeTask extends Task {

	private List<Task> tasks = new LinkedList<Task>();

	/**
	 * Default Constructor.
	 */
	public CompositeTask() {
		// do nothing
	}

	/**
	 * Adds the passed task object to this composite. It can be a primitive
	 * or composite task.
	 * 
	 * @param task The {@code Task} object to add.
	 */
	public void add(Task task) {
		// If composite, it will add its own tasks to itself.
		// Any composites there will do the same recursively.
		task.setup();
		// Now add the task so we can traverse them for execution.
		tasks.add(task);
	}

	@Override
	public Iterator<Task> iterator() {
		return tasks.iterator();
	}
	
	/**
	 * Schedules subordinate tasks for execution.
	 * 
	 * The job of run() in a composite is to schedule its
	 * subordinate tasks in an Executor Service.
	 * <p>
	 * Any subtasks that are themselves composites will run this
	 * method recursively until all primitive threads start.
	 * Once scheduled, each task will store its corresponding
	 * Future value so its life-cycle may be queried and controlled
	 * by the client code.
	 */
	@Override
    public void run() {
		try {
			int taskCount = tasks.size();
			ExecutorService exec = new TaskExecutor();
			final CountDownLatch ready = new CountDownLatch(taskCount);
			final CountDownLatch start = new CountDownLatch(1);
			final CountDownLatch finish = new CountDownLatch(taskCount);
			
			for (final Task task : tasks) {
				Future<?> future = exec.submit(new Runnable() {
					public void run() {
						try {
							ready.countDown();
							start.await();
							markStartTime();
							task.run();
						} catch (InterruptedException ie) {
							Thread.currentThread().interrupt();
						} finally {
							markFinishTime();
							finish.countDown();
						}
					}
				});
				task.setFuture(future);
			}
			ready.await();
			start.countDown();
			finish.await();
		} catch (InterruptedException ie) {
			Thread.currentThread().interrupt();
		}  
    }
	
}
